<?php
/**
 * OrangeHRM is a comprehensive Human Resource Management (HRM) System that captures
 * all the essential functionalities required for any enterprise.
 * Copyright (C) 2006 OrangeHRM Inc., http://www.orangehrm.com
 *
 * OrangeHRM is free software: you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * OrangeHRM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with OrangeHRM.
 * If not, see <https://www.gnu.org/licenses/>.
 */

namespace OrangeHRM\Tests\Core\Utility;

use DOMDocument;
use DOMElement;
use OrangeHRM\Core\Exception\SanitizerException;
use OrangeHRM\Core\Utility\Sanitizer;
use OrangeHRM\Tests\Util\TestCase;

class SanitizerTest extends TestCase
{
    private Sanitizer $sanitizer;
    private DOMDocument $domDocument;

    protected function setUp(): void
    {
        $this->sanitizer = new Sanitizer();
        $this->domDocument = new DOMDocument();
    }

    public function testSanitizeSVGWithScriptTag(): void
    {
        $maliciousSVG = <<<XML
            <svg version="1.1"
                baseProfile="full"
                xmlns="http://www.w3.org/2000/svg">
                <rect 
                    width="300"
                    height="100"
                    style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
                <script type="text/javascript">alert("This is stored cross-site scripting");</script>
            </svg>
        XML;

        $sanitizedSVG = $this->sanitizer->sanitizeSvg($maliciousSVG);
        $this->domDocument->loadXML($sanitizedSVG);

        $this->assertXmlStringNotEqualsXmlString($sanitizedSVG, $maliciousSVG);
        $this->assertEmpty($this->domDocument->getElementsByTagName('script'));
        $this->assertNotEmpty($this->domDocument->getElementsByTagName('rect'));
    }

    public function testSanitizeSVGWithOnClick(): void
    {
        $maliciousSVG = <<<XML
            <svg width="300"
                 height="130"
                 xmlns="http://www.w3.org/2000/svg"
                 onclick="alert(1)">
                <rect width="200"
                      height="100"
                      x="10"
                      y="10"
                      rx="20"
                      ry="20"
                      fill="blue" />        
                    Sorry, your browser does not support inline
                    SVG.
            </svg>        
        XML;

        $sanitizedSVG = $this->sanitizer->sanitizeSvg($maliciousSVG);
        $this->domDocument->loadXML($sanitizedSVG);

        $this->assertXmlStringNotEqualsXmlString($sanitizedSVG, $maliciousSVG);

        /** @var DOMElement $domElement */
        $domElement = $this->domDocument->getElementsByTagName('svg')[0];
        $this->assertTrue($domElement->hasAttribute('width'));
        $this->assertTrue($domElement->hasAttribute('height'));
        $this->assertTrue($domElement->hasAttribute('xmlns'));
        $this->assertFalse($domElement->hasAttribute('onclick'));
    }

    public function testSanitizeSVGWithAnimate(): void
    {
        $maliciousSVG = <<<XML
            <svg version="1.1"
                 baseProfile="full"
                 xmlns="http://www.w3.org/2000/svg">
                <rect width="300"
                      height="100"
                      style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)" />
                <animate onbegin="alert(1)"
                         attributeName="x"
                         dur="1s" />
            </svg>    
        XML;

        $sanitizedSVG = $this->sanitizer->sanitizeSvg($maliciousSVG);
        $this->domDocument->loadXML($sanitizedSVG);

        $this->assertXmlStringNotEqualsXmlString($sanitizedSVG, $maliciousSVG);
        $this->assertEmpty($this->domDocument->getElementsByTagName('animate'));
        $this->assertNotEmpty($this->domDocument->getElementsByTagName('rect'));
    }

    public function testSanitizeSVGWithRegularSVG(): void
    {
        $orangeHRMLogoSVG = <<<XML
            <svg id="Layer_1"
                 data-name="Layer 1"
                 xmlns="http://www.w3.org/2000/svg"
                 viewBox="0 0 750.11 189.77">
                <defs>
                    <style>.cls-1{fill:#f58321;}.cls-2{fill:#09a752;}.cls-3{fill:#231f20;}.cls-4{fill:#b0dcc4;}</style>
                </defs>
                <title>OrangeHRM_Logo</title>
                <path class="cls-1"
                      d="M122.44,95.63c3.45-1.3,6.55-2.13,9.62-3,7-2.11,14-1.85,21.09-.32A68.39,68.39,0,0,1,180.72,105c8.7,6.69,14.34,15.5,17.58,25.9,6.42,20.66,1.91,45.49-12.25,62-13.32,15.53-30,24.87-50.4,27.47-17.57,2.23-34-1.37-49.53-9.71A77.26,77.26,0,0,1,58,185.26c-6.49-9.71-10.75-20.28-11.83-31.95-1.49-16.2,3.41-30.32,14.22-42.42a50.59,50.59,0,0,1,11.72-9.76,33.34,33.34,0,0,1,24.76-4.26c9.15,1.9,17.61,5.26,25,11.09,5.38,4.26,9.53,9.44,11.4,16.19a25.74,25.74,0,0,1,.6,2.74c.08.49.29,1.15-.45,1.36s-1.45.47-2-.26c-.46-.57-.89-1.18-1.37-1.73a47.88,47.88,0,0,0-31.31-16.48,33.48,33.48,0,0,0-9.22.21,19.68,19.68,0,0,0-11.07,5.78c-8.47,8.54-13.27,18.69-12.49,30.92.55,8.54,3.88,16.15,9,22.93a61.05,61.05,0,0,0,32.93,22.27c16.69,4.74,33,3.55,48.52-4.37,8-4.07,14.69-9.62,19.19-17.55a52.05,52.05,0,0,0,6.29-19,60.53,60.53,0,0,0,.11-15.45A31.11,31.11,0,0,0,175,119.89a55.3,55.3,0,0,0-22.77-16.79c-7.29-3-15-4.68-22.69-6.11C127.34,96.58,125.12,96.15,122.44,95.63Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-2"
                      d="M752.61,161.34c2.67-4.93,5.27-9.7,7.85-14.48q10.24-19,20.46-37.92a2.17,2.17,0,0,1,2.29-1.48c1.32.1,3-.6,3.91.18s.61,2.62.77,4q1.58,13.59,3.1,27.19c.74,6.53,1.41,13.07,2.17,19.6.82,7,1.6,14,2.66,21l.06.4c.26,1.92.27,1.89-1.69,1.65a44.47,44.47,0,0,0-10.44.12c-1.88.22-2,.19-2.12-1.75-.41-4.61-.72-9.23-1.11-13.84-.86-10-1.75-19.91-2.88-29.85-.08-.7-.18-1.4-.28-2.2-.72.4-.86,1-1.14,1.53-8,14.47-15.77,29-22.45,44.12-.5,1.12-1,1.74-2.36,1.62s-2.56.48-3.56-.11-1.21-2-1.72-3.11c-6.83-14.22-14.14-28.19-21.33-42.22-.2-.4-.3-.9-.88-1.13a33,33,0,0,0-.73,5.29c-1.11,10.34-2.31,20.67-3.08,31-.23,3.15-.27,6.3-.48,9.45-.07.95-.34,1.24-1.35,1.12a98.89,98.89,0,0,0-18.29-.08c-1.4.09-2.9.54-4.18.19s-1.67-2-2.43-3.08a347.7,347.7,0,0,0-21.67-28c-.91-1-1.86-2-3.13-3.41-.14,11.75.17,23,.83,34.49a55.21,55.21,0,0,0-14.38.16.76.76,0,0,1-.41-.79c.45-7.77.76-15.55.82-23.34.08-11.62.12-23.24-.19-34.85-.11-4.16-.38-8.32-.58-12.47-.06-1.29,0-1.39,1.3-1.32,6.52.33,13,0,19.54,0,1.68,0,3.35-.08,5-.18a35.3,35.3,0,0,1,14.28,1.73c7.37,2.66,10.59,8.13,10.27,15.73-.29,7.14-4.18,12-10.09,15.44a31.67,31.67,0,0,1-9.78,3.61c-.45.09-.94.05-1.31.41.68,1.62,22.56,29.23,24.69,31.08.38-2.48.76-4.85,1.11-7.21,1.43-9.62,2.9-19.24,4.26-28.87,1-6.84,1.79-13.7,2.65-20.55.45-3.66.83-7.33,1.27-11,.19-1.63.22-1.66,1.8-1.66s3.14-.6,4.37.07,1.48,2.47,2.13,3.79c8,16.26,16.49,32.26,25.12,48.19A4.13,4.13,0,0,0,752.61,161.34Zm-84.07-31.51c0,2.21,0,6.43,0,10.66,0,1.77,0,1.76,1.73,2A22.14,22.14,0,0,0,681.52,141c5.71-2.28,9.25-6.32,9.88-12.55.69-6.79-2.09-12.5-9.33-14.71a28.94,28.94,0,0,0-12.14-.7c-.91.11-.84.69-.86,1.29C668.92,118.84,668.77,123.33,668.54,129.83Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-3"
                      d="M439.06,181.33c-6.14-2-9.72-6-9.56-12.77.14-5.52,3.71-9.65,10.74-12.49-.33-.77-1.13-.84-1.73-1.16-6.65-3.51-11.19-8.71-12.66-16.23-1.29-6.61-.66-13,2.9-18.85s9.06-9,15.5-10.71A49.07,49.07,0,0,1,459.44,108c7.91.39,15.79,1.38,23.73,1,2.39-.11,4.78-.32,7.33-.5a34.64,34.64,0,0,0,0,5.39c.11,1-.64.8-1.21.76-3-.26-6-.56-9-.78a37.28,37.28,0,0,0-6.59-.27c-.3.63.27.69.54.89,6.27,4.55,9.27,10.82,9.52,18.43.24,7.13-2,13.35-7.4,18.21A26.74,26.74,0,0,1,464.15,157c-5,1.17-10.13,1-15.23.71a14.61,14.61,0,0,0-3.62.06c-5.67,1.19-8,6.7-5.26,11.87A5.23,5.23,0,0,0,443.2,172a17.31,17.31,0,0,0,5,.73c6.77.26,13.56.23,20.32.67a36.54,36.54,0,0,1,11.17,2.41c8,3.17,12.23,9.36,11.81,18-.61,12.57-6.89,21.47-18.16,26.85a40.05,40.05,0,0,1-16.4,3.59,52.34,52.34,0,0,1-18.27-2.35,29.17,29.17,0,0,1-8.35-4.35c-9.82-7.38-9.75-21.38-2.16-29.17a25.86,25.86,0,0,1,9.19-6C437.9,182.13,438.67,182.16,439.06,181.33Zm18,1.09c-.81,0-1.61,0-2.42,0-4.72.18-9.36.77-13.67,2.86a14.12,14.12,0,0,0-7.6,8.67,22.69,22.69,0,0,0-.88,8.35c.46,8.12,4.95,13.94,12.74,16.23a33.94,33.94,0,0,0,21.54-.59,19.78,19.78,0,0,0,11.54-9.75A20.39,20.39,0,0,0,480,197c-.52-6.2-3.55-10.51-9.52-12.67A40.48,40.48,0,0,0,457.06,182.42Zm-19.78-48.94a27,27,0,0,0,2,10.91,14.84,14.84,0,0,0,9.56,8.84,19.49,19.49,0,0,0,4.55.75c6.28.41,11.48-1.69,14.66-7.22a27.43,27.43,0,0,0,1.34-25.86,14.18,14.18,0,0,0-10.51-8.26,19.35,19.35,0,0,0-6.61-.25,15.43,15.43,0,0,0-11.88,7.67C438,124.05,437.24,128.42,437.28,133.48Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-2"
                      d="M168,46.51A227,227,0,0,0,139,61.24a140.77,140.77,0,0,0-20.05,14.15c-1.26,1.09-2.49,2.21-3.94,3.5a15.68,15.68,0,0,1,0-4.27,51.28,51.28,0,0,1,4.33-17.69,29.4,29.4,0,0,1,16.48-15.68,97.77,97.77,0,0,1,25.57-6,132.65,132.65,0,0,1,18.9-.64,75.37,75.37,0,0,1,10.33.88,1,1,0,0,1,0,1c-5.79,12-12,23.72-20,34.42-1,1.29-2,2.53-3,3.77A25.9,25.9,0,0,1,155.19,83a116.28,116.28,0,0,1-19.84,4.25c-3.18.42-6.38.77-9.58,1.16a4,4,0,0,1-1.68,0A5.86,5.86,0,0,1,125.27,86c4-6.59,9.27-12.15,14.92-17.35,8.27-7.62,17.4-14.13,26.67-20.44.55-.38,1.09-.78,1.64-1.18a1.7,1.7,0,0,0,1.28-1.2A2.29,2.29,0,0,0,168,46.51Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-2"
                      d="M593.82,108.83c0,2.21.09,4.22,0,6.22-.4,7.65-.62,15.29-.59,23,0,1.77,0,1.76,1.49,1.81,11.68.43,23.36.32,35,.2,3.39,0,3.39-.06,3.38-3.46,0-8.6-.45-17.18-.78-25.76,0-.53,0-1.05,0-1.8a51.86,51.86,0,0,0,14.5-.2c.07,6.06-.5,12-.58,18s-.15,12.21-.15,18.32,0,12.09.16,18.13.44,12.05.67,18.14c-1.36.34-2.51-.17-3.69-.19a64.73,64.73,0,0,0-9.64.39c-1.3.16-1.33.09-1.28-1.24.29-7.78.7-15.56.77-23.35,0-3.56,0-7.12,0-10.67,0-1.42,0-1.46-1.44-1.47-12.36-.13-24.71-.44-37.06,0-1.38,0-1.42,0-1.41,1.47,0,7.86-.08,15.71.37,23.56.22,3.81.29,7.62.43,11.48-1.28.42-2.44-.15-3.61-.17a60.71,60.71,0,0,0-9.44.37c-1.6.22-1.58,0-1.52-1.45.16-3.69.31-7.37.42-11.06.5-16.18.44-32.36,0-48.54-.1-3.42-.3-6.84-.41-10.26,0-1.26,0-1.34,1.35-1.18A51.39,51.39,0,0,0,593.82,108.83Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-3"
                      d="M343.44,109.39a29,29,0,0,0,12.65,0c.45,2.08-.15,4-.14,6s0,4,0,6.17c.86-.38,1.13-1.09,1.53-1.64,3.36-4.6,7.47-8.28,12.88-10.25a29.68,29.68,0,0,1,21.26,0c6.63,2.5,10.65,7.48,12.63,14.18a38.83,38.83,0,0,1,1.43,11.94c-.28,12.42-.81,24.83-.61,37.25.05,2.82.16,5.64.23,8.45a2,2,0,0,1-.28,1.54,26.63,26.63,0,0,0-12.11,0c0-2.66-.08-5.19,0-7.72.47-12.14,1-24.28.79-36.44a38.29,38.29,0,0,0-1.63-11.1,20.12,20.12,0,0,0-2.72-5.59,14.44,14.44,0,0,0-11.1-6.41,24,24,0,0,0-7.8.61c-6.64,1.64-10.79,6-12.88,12.35a40.52,40.52,0,0,0-1.72,12.71c0,10.94-.09,21.89.52,32.82.15,2.48.4,5,.59,7.42,0,.39,0,.78,0,1.31a55.21,55.21,0,0,0-6.87-.67,49,49,0,0,0-6.69.69A566.55,566.55,0,0,0,343.44,109.39Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-3"
                      d="M540.69,145c-8.39-.17-16.78-.2-25.17.26-1.56.08-1.58.08-1.65,1.51a36,36,0,0,0,2.06,15.05c3.65,9.3,10.4,14.77,20.33,16.14s18.4-2.33,25.78-8.81c.45-.4.85-.85,1.29-1.27.24-.23.48-.51.85-.28s.32.56.28.9c-.23,1.93-.48,3.86-.67,5.79a2.77,2.77,0,0,1-1.51,2,43.26,43.26,0,0,1-18.56,7.84c-12.06,1.84-23-.91-32.15-9.14a33.09,33.09,0,0,1-10.87-21.22,50.32,50.32,0,0,1,3.59-26.61c4.74-10.94,13.21-17.14,24.94-18.9a39.92,39.92,0,0,1,17.36,1c11.66,3.44,18.24,11.46,20.47,23.25a46.12,46.12,0,0,1,.37,11.44c-.08,1.36-.12,1.39-1.57,1.37-4.5-.07-9-.21-13.49-.26C548.48,145,544.58,145,540.69,145Zm-6.17-5,18.92-.42c1.56,0,1.6-.08,1.57-1.57a36.61,36.61,0,0,0-3.23-15.63,16.43,16.43,0,0,0-7.09-7.71,18.85,18.85,0,0,0-23.16,4.76c-4.4,5.38-6.31,11.74-7.11,18.51-.18,1.55-.16,1.59,1.38,1.63C522,139.76,528.28,139.89,534.52,140Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-3"
                      d="M325.92,177.82c.18,3.35.53,3-2.47,4a22.76,22.76,0,0,1-7.51,1.2c-5.33-.07-8.82-2.85-10.11-8.05-.1-.39-.22-.77-.35-1.24-.73.18-1.09.77-1.54,1.21a36.17,36.17,0,0,1-8.66,6.51c-7.49,3.74-15.25,4.75-23.1,1.33-6.89-3-11-8.3-11.41-15.95-.45-7.9,2.58-14.13,9.55-18.19a36.78,36.78,0,0,1,12.56-4.08c5.48-.93,11-1.66,16.36-3.22a15.61,15.61,0,0,0,3.73-1.48,4.89,4.89,0,0,0,2.63-4.23,27.43,27.43,0,0,0-1.28-10.72c-2.09-5.89-6.15-9.56-12.39-10.41A23.48,23.48,0,0,0,274.06,119a17.43,17.43,0,0,0-4.43,4.9c-.28.47-.53.92-1.2.89-.52,0-.93-.07-.93-.74,0-2.21,0-4.43,0-6.64a1.38,1.38,0,0,1,.71-1.14c9.59-7.16,20.2-10.25,32-7.42,8.39,2,14.09,7.21,16.41,15.74.84,3.08.73,6.24.7,9.4-.11,11.21-.64,22.41-.56,33.62a27.66,27.66,0,0,0,.35,5.22c.83,4.14,3,5.74,7.11,5.26C324.75,178,325.27,177.91,325.92,177.82Zm-20.41-33.28a1.21,1.21,0,0,0-1,0c-4.41,1.15-8.92,1.85-13.37,2.8a41.31,41.31,0,0,0-9.16,2.94c-6.1,2.89-9.06,7.66-8.73,14.48.42,9,7.52,14.78,16.39,13.3,6-1,10.6-4.26,14.3-9a5.47,5.47,0,0,0,1.19-3.49c.11-6.5.3-13,.46-19.51C305.63,145.58,305.81,145,305.51,144.54Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-3"
                      d="M228.46,183.08a26.5,26.5,0,0,0-12,.07c.62-24.58.63-49.05-.12-73.74a29,29,0,0,0,12.45,0,109.47,109.47,0,0,0-.6,14.93c.74-.37.88-1,1.16-1.46A38,38,0,0,1,238.21,112c3.4-2.72,7.2-4.4,11.67-4.17a14.08,14.08,0,0,1,3,.57c.62.17.82.53.65,1.2a39,39,0,0,0-.79,9.4c0,.34,0,.68,0,1-.07,1.74-.49,1.94-2,1.16a13.53,13.53,0,0,0-13.64.21,16,16,0,0,0-6.57,7.93,30.56,30.56,0,0,0-2.24,12.39c0,8.06.1,16.11.11,24.17,0,5.44,0,10.88.46,16.3A.94.94,0,0,1,228.46,183.08Z"
                      transform="translate(-45.89 -34.52)" />
                <path class="cls-4"
                      d="M168,46.51a2.29,2.29,0,0,1,1.76-.72A1.7,1.7,0,0,1,168.5,47Z"
                      transform="translate(-45.89 -34.52)" />
            </svg>
        XML;

        $sanitizedSVG = $this->sanitizer->sanitizeSvg($orangeHRMLogoSVG);

        $this->assertXmlStringEqualsXmlString($sanitizedSVG, $orangeHRMLogoSVG);
    }

    public function testSanitizeSVGWithBadlyFormattedSVG(): void
    {
        $badlyFormattedSVG = <<<XML
            <svg version="1.1"
                 baseProfile="full"
                 xmlns="http://www.w3.org/2000/svg">
                <rect width="300"
                      height="100"
                      style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)"
            </svg>    
        XML;

        $this->expectException(SanitizerException::class);
        $this->expectExceptionMessage('SVG is badly formatted.');
        $this->sanitizer->sanitizeSvg($badlyFormattedSVG);
    }
}
